package ncme.auto.framework.core.aspects;

import ncme.auto.framework.core.annotation.Remind;
import ncme.auto.framework.core.annotation.aspect.AutoMapperProcess;
import ncme.auto.framework.core.annotation.entity.Primary;
import ncme.auto.framework.core.annotation.entity.Secondary;
import ncme.auto.framework.core.aspects.base.BaseAutoAspect;
import ncme.auto.framework.core.entities.ResMsg;
import ncme.auto.framework.core.entities.aspect.AspectInfo;
import ncme.auto.framework.core.entities.aspect.MapperInfo;
import ncme.auto.framework.core.entities.aspect.RequestInfo;
import ncme.auto.framework.core.enums.MapperAutoOpt;
import ncme.auto.framework.core.exceptions.AutoException;
import ncme.auto.framework.core.exceptions.ExceptionEnum;
import ncme.auto.framework.core.services.Impl.base.BaseAutoMapperServiceImpl;
import ncme.auto.framework.core.services.Impl.base.BaseReflectService;
import ncme.auto.framework.core.services.Impl.mian.ManyToManyAutoMapperImpl;
import ncme.auto.framework.core.services.Impl.mian.OneToManyAutoMapperImpl;
import ncme.auto.framework.core.services.Impl.mian.OneToOneAutoMapperImpl;
import ncme.auto.framework.core.utils.BeanUtil;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.Collections;
import java.util.List;
import java.util.Map;

@Aspect
@Component
@Remind("对Entity命名有要求；A、B、AB 分别为主从两个实体和mapper关系" +
        "请求均为批量处理" +
        "Get  请求时 primary 赋值 查询 secondary 数据，反过来也是，注解中 ForEntity 字段决定返回的是 id 还是 实体" +
        "Post 请求中 暂时限定 entity 除了 primary 和 secondary 以外没有其他必填项，比如creator; 可以主加从，也可以从加主，但不可以 混加" +
        "Edit 请求主要处理批量更改" +
        "Del  请求 处理单条数据")
public class AutoMapperProcessAspect extends BaseAutoAspect<AutoMapperProcess> {

    @Pointcut("@annotation(com.hby.common.annotation.aspect.AutoMapperProcess)")
    public void aspectCut() {
    }

    @Override
    protected void parameterPreHandler(AspectInfo aspectInfo) {
        Map<String, Object> parameter = aspectInfo.getParameter1();
        MapperInfo mapperInfo = aspectInfo.getMapperInfo();
        //验证注解规范
        List<Field> primaryFields = BeanUtil.getFieldsWithAnnotation(Primary.class, aspectInfo.getAnnotationInfo().getAutoMapperProcess().MAPPER_ENTITY());
        List<Field> secondaryFields = BeanUtil.getFieldsWithAnnotation(Secondary.class, aspectInfo.getAnnotationInfo().getAutoMapperProcess().MAPPER_ENTITY());
        if (primaryFields.size() != 1 || secondaryFields.size() != 1)
            throw new AutoException(ExceptionEnum.ANNOTATION, "mapper实体含有零或多个Primary或Secondary注解字段");
        //获取映射主从字段
        mapperInfo.setPrimaryField(primaryFields.get(0).getName());
        mapperInfo.setSecondaryField(secondaryFields.get(0).getName());
        //获取映射信息
        try {
            mapperInfo.setHasPrimaryId(parameter.containsKey(mapperInfo.getPrimaryField()));
            if (mapperInfo.hasPrimaryId()) {
                mapperInfo.setPrimaryId(Integer.parseInt(parameter.get(mapperInfo.getPrimaryField()).toString()));
            }
            mapperInfo.setHasPrimaryIds(parameter.containsKey(mapperInfo.getPrimaryField() + "s"));
            if (mapperInfo.hasPrimaryIds()) {
                mapperInfo.setPrimaryIds((List<Integer>) parameter.get(mapperInfo.getPrimaryField() + "s"));
            }
            mapperInfo.setHasSecondaryId(parameter.containsKey(mapperInfo.getSecondaryField()));
            if (mapperInfo.hasSecondaryId()) {
                mapperInfo.setSecondaryId(Integer.parseInt(parameter.get(mapperInfo.getSecondaryField()).toString()));
            }
            mapperInfo.setHasSecondaryIds(parameter.containsKey(mapperInfo.getSecondaryField() + "s"));
            if (mapperInfo.hasSecondaryIds())
                mapperInfo.setSecondaryIds((List<Integer>) parameter.get(mapperInfo.getSecondaryField() + "s"));
        } catch (Exception e) {
            throw new AutoException(ExceptionEnum.PARAM_FIELD, "请检查：Id对应Integer类型，Ids对应Integer[]类型");
        }
    }

    @Override
    protected void parameterVerify(AspectInfo aspectInfo) {
        RequestInfo requestInfo = aspectInfo.getRequestInfo();
        MapperInfo mapperInfo = aspectInfo.getMapperInfo();
        BaseReflectService<?> baseAutoService = aspectInfo.getBaseAutoService();
        String primaryField = mapperInfo.getPrimaryField();
        String secondaryField = mapperInfo.getSecondaryField();
        boolean hasPrimaryId = mapperInfo.hasPrimaryId();
        boolean hasPrimaryIds = mapperInfo.hasPrimaryIds();
        boolean hasSecondaryId = mapperInfo.hasSecondaryId();
        boolean hasSecondaryIds = mapperInfo.hasSecondaryIds();
        if ((hasPrimaryId && hasPrimaryIds) || (hasSecondaryId && hasSecondaryIds))
            throw new AutoException(ExceptionEnum.PARAM_FIELD, "映射参数冗余");
        switch (requestInfo.getRequestType()) {
            case "GET":
                if (hasPrimaryId && !hasSecondaryId) mapperInfo.setParamConstruct(MapperAutoOpt.ONLY_P_Id);
                else if (!hasPrimaryId && hasSecondaryId) mapperInfo.setParamConstruct(MapperAutoOpt.ONLY_S_Id);
                else
                    throw new AutoException(ExceptionEnum.PARAM_FIELD, "参数应包含且仅包含" + primaryField + "或" + secondaryField + "的一项");
                break;
            case "POST":
            case "DELETE":
                if (baseAutoService instanceof OneToOneAutoMapperImpl) {
                    if (hasPrimaryId & hasSecondaryId) mapperInfo.setParamConstruct(MapperAutoOpt.PId_And_SId);
                    else
                        throw new AutoException(ExceptionEnum.PARAM_FIELD, "参数应同时包含" + primaryField + "和" + secondaryField);
                } else if (baseAutoService instanceof OneToManyAutoMapperImpl) {
                    if (hasPrimaryId && hasSecondaryId) mapperInfo.setParamConstruct(MapperAutoOpt.PId_And_SId);
                    else if (hasPrimaryId && hasSecondaryIds)
                        mapperInfo.setParamConstruct(MapperAutoOpt.PId_And_SIds);
                    else
                        throw new AutoException(ExceptionEnum.PARAM_FIELD, "参数应同时包含" + primaryField + "和" + secondaryField + "/" + secondaryField + "s");
                } else if (baseAutoService instanceof ManyToManyAutoMapperImpl) {
                    if (hasPrimaryId && hasSecondaryId) mapperInfo.setParamConstruct(MapperAutoOpt.PId_And_SId);
                    else if (hasPrimaryId && hasSecondaryIds) mapperInfo.setParamConstruct(MapperAutoOpt.PId_And_SIds);
                    else if (hasSecondaryId && hasPrimaryIds) mapperInfo.setParamConstruct(MapperAutoOpt.PIds_And_SId);
                    else
                        throw new AutoException(ExceptionEnum.PARAM_FIELD, "参数应同时包含{" + primaryField + "/" + primaryField + "s和" + secondaryField + "/" + secondaryField + "s");
                }
                break;
            case "PUT":
                if (baseAutoService instanceof OneToOneAutoMapperImpl) {
                    if (hasPrimaryId && hasSecondaryId) mapperInfo.setParamConstruct(MapperAutoOpt.PId_And_SId);
                    else
                        throw new AutoException(ExceptionEnum.PARAM_FIELD, "参数应同时包含" + primaryField + "和" + secondaryField);
                } else if (baseAutoService instanceof OneToManyAutoMapperImpl) {
                    if (hasPrimaryId && hasSecondaryId) mapperInfo.setParamConstruct(MapperAutoOpt.PId_And_SId);
                    else if (hasPrimaryId && hasSecondaryIds) mapperInfo.setParamConstruct(MapperAutoOpt.PId_And_SIds);
                    else
                        throw new AutoException(ExceptionEnum.PARAM_FIELD, "参数应同时包含{" + primaryField + "和" + secondaryField + "/" + secondaryField + "s}");
                } else if (baseAutoService instanceof ManyToManyAutoMapperImpl) {
                    if (hasPrimaryId && hasSecondaryIds) mapperInfo.setParamConstruct(MapperAutoOpt.PId_And_SIds);
                    else if (hasPrimaryIds && hasSecondaryId) mapperInfo.setParamConstruct(MapperAutoOpt.PIds_And_SId);
                    else
                        throw new AutoException(ExceptionEnum.PARAM_FIELD, "参数应同时包含{" + primaryField + "和" + secondaryField + "s}或{" + primaryField + "s和" + secondaryField + "}");
                }
                break;
            default:
                throw new AutoException(ExceptionEnum.SERVICE_OTHER, "未知请求类型");
        }
        if (mapperInfo.getParamConstruct().equals(MapperAutoOpt.NONE))
            throw new AutoException(ExceptionEnum.PARAM_FIELD, "未知映射参数结构");
    }

    @Override
    protected ResMsg autoProcess(AspectInfo aspectInfo) {
        Object result = null;
        RequestInfo requestInfo = aspectInfo.getRequestInfo();
        MapperInfo mapperInfo = aspectInfo.getMapperInfo();
        MapperAutoOpt paramConstruct = mapperInfo.getParamConstruct();
        BaseAutoMapperServiceImpl<?, ?, ?> baseAutoMapperService = (BaseAutoMapperServiceImpl<?, ?, ?>) aspectInfo.getBaseAutoService();
        boolean forEntity = aspectInfo.getAnnotationInfo().getAutoMapperProcess().FOR_ENTITY();
        switch (requestInfo.getRequestType()) {
            case "GET":
                if (paramConstruct.equals(MapperAutoOpt.ONLY_P_Id)) {
                    if (forEntity) result = baseAutoMapperService.getEntitiesFromId(mapperInfo.getPrimaryId(), true);
                    else result = baseAutoMapperService.getIdsFromId(mapperInfo.getPrimaryId(), true);
                } else if (paramConstruct.equals(MapperAutoOpt.ONLY_S_Id)) {
                    if (forEntity) result = baseAutoMapperService.getEntitiesFromId(mapperInfo.getSecondaryId(), false);
                    else result = baseAutoMapperService.getIdsFromId(mapperInfo.getSecondaryId(), false);
                }
                break;
            case "POST":
                if (baseAutoMapperService instanceof OneToOneAutoMapperImpl) {
                    OneToOneAutoMapperImpl<?, ?, ?> service = (OneToOneAutoMapperImpl<?, ?, ?>) baseAutoMapperService;
                    if (paramConstruct.equals(MapperAutoOpt.PId_And_SId))
                        service.addSecondaryIdForPrimaryId(mapperInfo.getPrimaryId(), mapperInfo.getSecondaryId());
                } else if (baseAutoMapperService instanceof OneToManyAutoMapperImpl) {
                    OneToManyAutoMapperImpl<?, ?, ?> service = (OneToManyAutoMapperImpl<?, ?, ?>) baseAutoMapperService;
                    if (paramConstruct.equals(MapperAutoOpt.PId_And_SId))
                        service.addSecondaryIdForPrimaryId(mapperInfo.getPrimaryId(), mapperInfo.getSecondaryId());
                    else if (paramConstruct.equals(MapperAutoOpt.PId_And_SIds))
                        service.addSecondaryIdsForPrimaryId(mapperInfo.getPrimaryId(), mapperInfo.getSecondaryIds());
                } else if (baseAutoMapperService instanceof ManyToManyAutoMapperImpl) {
                    ManyToManyAutoMapperImpl<?, ?, ?> service = (ManyToManyAutoMapperImpl<?, ?, ?>) baseAutoMapperService;
                    if (paramConstruct.equals(MapperAutoOpt.PId_And_SId))
                        service.addSecondaryIdsForPrimaryId(mapperInfo.getPrimaryId(), Collections.singletonList(mapperInfo.getSecondaryId()));
                    else if (paramConstruct.equals(MapperAutoOpt.PId_And_SIds))
                        service.addSecondaryIdsForPrimaryId(mapperInfo.getPrimaryId(), mapperInfo.getSecondaryIds());
                    else if (paramConstruct.equals(MapperAutoOpt.PIds_And_SId))
                        service.addPrimaryIdsForSecondaryId(mapperInfo.getSecondaryId(), mapperInfo.getPrimaryIds());
                }
                break;
            case "PUT":
                if (baseAutoMapperService instanceof OneToOneAutoMapperImpl) {
                    OneToOneAutoMapperImpl<?, ?, ?> service = (OneToOneAutoMapperImpl<?, ?, ?>) baseAutoMapperService;
                    if (paramConstruct.equals(MapperAutoOpt.PId_And_SId))
                        service.editSecondaryIdOfPrimaryId(mapperInfo.getPrimaryId(), mapperInfo.getSecondaryId());  //目前只能修改primary对应的secondary映射
                } else if (baseAutoMapperService instanceof OneToManyAutoMapperImpl) {
                    OneToManyAutoMapperImpl<?, ?, ?> service = (OneToManyAutoMapperImpl<?, ?, ?>) baseAutoMapperService;
                    if (paramConstruct.equals(MapperAutoOpt.PId_And_SId))  //修改secondary对应的primary映射
                        service.editPrimaryIdOfSecondaryId(mapperInfo.getSecondaryId(), mapperInfo.getPrimaryId());
                    else if (paramConstruct.equals(MapperAutoOpt.PId_And_SIds))  //修改primary对应的secondary映射
                        service.editSecondaryIdsOfPrimaryId(mapperInfo.getPrimaryId(), mapperInfo.getSecondaryIds());
                } else if (baseAutoMapperService instanceof ManyToManyAutoMapperImpl) {
                    ManyToManyAutoMapperImpl<?, ?, ?> service = (ManyToManyAutoMapperImpl<?, ?, ?>) baseAutoMapperService;
                    if (paramConstruct.equals(MapperAutoOpt.PId_And_SIds))
                        service.editSecondaryIdsOfPrimaryId(mapperInfo.getPrimaryId(), mapperInfo.getSecondaryIds());
                    else if (paramConstruct.equals(MapperAutoOpt.PIds_And_SId))
                        service.editPrimaryIdsOfSecondaryId(mapperInfo.getSecondaryId(), mapperInfo.getPrimaryIds());
                }
                break;
            case "DELETE":
                if (baseAutoMapperService instanceof OneToOneAutoMapperImpl) {
                    OneToOneAutoMapperImpl<?, ?, ?> service = (OneToOneAutoMapperImpl<?, ?, ?>) baseAutoMapperService;
                    if (paramConstruct.equals(MapperAutoOpt.PId_And_SId))
                        service.deleteSecondaryIdOfPrimaryId(mapperInfo.getPrimaryId(), mapperInfo.getSecondaryId());
                } else if (baseAutoMapperService instanceof OneToManyAutoMapperImpl) {
                    OneToManyAutoMapperImpl<?, ?, ?> service = (OneToManyAutoMapperImpl<?, ?, ?>) baseAutoMapperService;
                    if (paramConstruct.equals(MapperAutoOpt.PId_And_SId))
                        service.deleteSecondaryIdsOfPrimaryId(mapperInfo.getPrimaryId(), Collections.singletonList(mapperInfo.getSecondaryId()));
                    else if (paramConstruct.equals(MapperAutoOpt.PId_And_SIds))
                        service.deleteSecondaryIdsOfPrimaryId(mapperInfo.getPrimaryId(), mapperInfo.getSecondaryIds());
                } else if (baseAutoMapperService instanceof ManyToManyAutoMapperImpl) {
                    ManyToManyAutoMapperImpl<?, ?, ?> service = (ManyToManyAutoMapperImpl<?, ?, ?>) baseAutoMapperService;
                    if (paramConstruct.equals(MapperAutoOpt.PId_And_SId))
                        service.deleteSecondaryIdsOfPrimaryId(mapperInfo.getPrimaryId(), Collections.singletonList(mapperInfo.getSecondaryId()));
                    else if (paramConstruct.equals(MapperAutoOpt.PId_And_SIds))
                        service.deleteSecondaryIdsOfPrimaryId(mapperInfo.getPrimaryId(), mapperInfo.getSecondaryIds());
                    else if (paramConstruct.equals(MapperAutoOpt.PIds_And_SId))
                        service.deletePrimaryIdsOfSecondaryId(mapperInfo.getSecondaryId(), mapperInfo.getPrimaryIds());
                }
                break;
            default:
                throw new AutoException(ExceptionEnum.PARAM_FIELD, "未知请求类型");
        }
        return ResMsg.successRes(result);
    }

}
